fix(agent): sandbox process execution - #5818
Conversation
8df2141 to
d4ce8ce
Compare
|
re-reviewed at 28342d3 after the rebase. most of what i had is closed.
that last one also closes something bigger by accident. teacher takeover used to start a fresh worth knowing it holds by inference rather than propagation, though. anything that later rebuilds history without metadata, or hands the teacher reconstructed text, silently reopens it. the kwarg already exists and forwarding it is one line, so i'd do that as a belt. the overlay contract
the list is also incomplete, and interestingly this repo disagrees with itself about it. extending the list doesn't fix it, because the gap isn't membership. two test oracles
two are still green regardless of the property:
smallera file-backed detached jobs get per-process prlimits and no aggregate cap. three distinct
reviewed on macOS, so the argv is read and the Linux runtime behaviour is unverified here. #6021 covers the scheduled-action path separately, since none of those files are in this diff. |
28342d3 to
ee2e71b
Compare
ee2e71b to
527d01d
Compare
5494bee to
f655450
Compare
|
Changed-file classification: UI-sensitive. Author-reported runtime / visual state
Checkboxes are author attestations. GitHub Actions results remain the execution evidence for CI; this check does not prove that a local command ran. This comment updates automatically when the description or changed files change. |
Route foreground Bash, Python, tmux sessions, and detached jobs through one positive-mount bubblewrap profile. Clear inherited environment and network access, protect credentials and repository metadata, hide Odysseus data roots, and apply bounded resources while preserving one writable workspace.
f655450 to
a9f20e0
Compare
StressTestor
left a comment
There was a problem hiding this comment.
re-reviewed at 87b93e6. the overlay list, process-home rejection, sqlite DATABASE_URL mask and the network-namespace oracle close my 8/13 items.
residual: masks are built at launch, so a .bash_login that doesn't exist yet has none. reproduced: .bashrc present gets EACCES and stays intact on the host; .profile and .bash_login written in the same run land on it. needs the workspace to be another login home, so follow-up.
shipped runtime (your item 3)
slim image off the same python:3.14-slim base, PR apt set, make -C security/{seccomp,egress} install, run on Ubuntu 24.04 (6.8) / docker 28 with compose's security_opt: seccomp=..., as the dropped uid. bwrap never starts. three layers:
Failed to make / slave: Permission denied. docker-default AppArmor deniesmount. the seccomp profile works; docker's stock one fails earlier at namespace creation.apparmor=unconfined:loopback: Failed RTM_NEWADDR: Operation not permitted. natively on the same VM, unprivileged bwrap works atkernel.apparmor_restrict_unprivileged_userns=0and fails exactly so at Ubuntu's default 1.- root +
CAP_SYS_ADMIN+apparmor=unconfined:Can't mount proc on /newroot/proc: Operation not permitted. docker masks/procpaths; procfs won't mount in a userns unless the parent's is fully visible. dropping--proc /proc(execution_sandbox.py:533) fixes that run,systempaths=unconfinedis the other exit. THREAT_MODEL.md says the sandbox omits /proc; the code mounts it. the docker constraint favors the doc.
so every bash/python/tmux/bg call is BLOCKED on this host class. fail-closed holds, nothing gets weaker by merging, but compose ships a sandbox that can't start on an Ubuntu 24.04 docker host, and the one-line workaround is privileged: true.
focused suite: 100 passed / 4 failed / 29 errors under the shipped posture; 130 / 3 under root + SYS_ADMIN + apparmor=unconfined + systempaths=unconfined, the only posture short of --privileged that started (the 3: no tmux in my slim image, plus the two test items below). no unprivileged uid started inside docker here at all, so that half wants a bare Ubuntu docker host. CI installs no bubblewrap, hence your 29 skips.
before landing i'd want an in-repo AppArmor profile (userns, mount, pivot_root, per Ubuntu's bwrap-userns-restrict) wired into compose, the /proc call made with doc and code agreeing, the sysctl documented, and a boot-time bwrap ... /bin/true self-test that logs loudly.
small
odysseus_egress_bridge.py:190-191appends the worker beforestart();test_loopback_bridge_releases_a_slot_when_the_broker_closes_firstjoins it unstarted (cannot join thread before it is started). 4/4 full-file runs on 2 cpus, passes alone.test_sandbox_network_namespace_has_no_external_routehardcodes/usr/bin/python3; the base image only has/usr/local/bin/python3, so it fails rather than skips.- detached jobs: per-process prlimits, no aggregate cap.
code's fine by me. i'd hold the merge until the compose posture starts the sandbox somewhere that isn't --privileged.
|
Caveat: I still need to test via docker, previously tested with podman, which has a default AppArmor profile |
|
The 11k-line cumulative branch has now been split into four independently owned draft PRs: #6119 (seccomp substrate), #6118 (egress), #6120 (process core), and #6121 (container/runtime integration). The updated body records the dependency order and the remaining real-host validation gap; discussion and commit history remain available here for context. This PR is being closed as superseded by those public replacements. |
Summary
Routes foreground Bash, Python, tmux sessions, and detached background jobs through one Linux Bubblewrap boundary. Sandbox remains the default process-execution mode: Odysseus probes the real process boundary under the serving user. A capability failure blocks only Bash, Python, tmux, and detached jobs; there is no automatic downgrade. An admin may transiently enable Full Access only after the warning and exact typed confirmation
ENABLE FULL ACCESS. Full Access expands filesystem authority to the service-user view but retains the private PID/network boundary. Internet remains networkless by default and brokered public HTTP(S) when enabled; raw container networking is never exposed.The boundary fails closed when it cannot be established, when the selected workspace cannot be safely constrained, when the trusted launcher or broker configuration is invalid, or when Sandbox mode is unavailable on the current platform. The current per-process limits are: 4 GiB virtual address space, 1 hour CPU time, 4 GiB file-size maximum, and 1,024 file descriptors; there is no hard PID/thread ceiling in this slice. Aggregate agent and disk quotas are deferred. This PR adds only the process-execution Sandbox/Full Access mode. It still does not change the automatic action-approval default or implement the broader Ask/exact-approval authority model owned by #6084 and #5819.
Stack
This cumulative process-boundary PR is superseded by the focused replacement chain below. It remains available as source context for the split; its discussion and commit history remain available, and no branch rewrite or new implementation is planned here.
Superseded map
The cumulative branch is replaced by four independently owned draft PRs in this order:
The process-sandbox tracker is #6091. The authority follow-up remains PR #5819 for Issue #6092; downstream PRs are #6084, #5821, #6100, and #6104. #6119 and #6118 are independently reviewable substrates; #6120 consumes both; #6121 integrates all three for shipped container posture.
PR #6121's standalone image/Trivy check is expected to remain blocked against
devuntil #6119 lands. Real Ubuntu Docker/Compose/AppArmor host validation remains required and is not claimed by the replacement chain yet.Target branch
dev, notmain. All PRs land indev;mainis curated by the maintainer at each release. If your PR is onmainby accident, click "Edit" on this PR and change the base.Linked Issue
Part of #6091
Part of #5815
Superseded by PR #6119, PR #6118, PR #6120, and PR #6121.
Part of #4754
Related: #6083, #6084, #3709, and #6021
Follow-up: #6099, #6100, and #6104
Follow-up: Linear SMA-76 for aggregate agent CPU/RAM/PID ceilings and workspace-disk quotas.
Type of Change
Checklist
devdocker compose uporuvicorn app:app) and verified the change works end-to-end. Type-checks and unit tests are not enough.How to Test
python -m pytest -q tests/test_process_execution_mode.py tests/test_process_execution_routes.py tests/test_execution_sandbox.py tests/test_process_sandbox_dispatch.py tests/test_bg_job_launch_cleanup.py tests/test_sandbox_network_policy.py tests/test_egress_broker.py tests/test_agent_bash_windows.py tests/test_foreground_model_routing.py tests/test_workspace_confine.py tests/test_seccomp_generator.py tests/test_seccomp_launcher.py tests/test_seccomp_policy.py; the focused process-mode and sandbox tests cover the process authority, dispatch, network, broker, and workspace-boundary paths.python -m compileall -q core routes src, the seccomp generator/build checks, andgit diff --check origin/dev...HEAD. Current CI evidence for headb3994bd396b8eb35fa66df252006ad0a6f66ba19: full pytest5903 passed, 49 skipped, 9 warnings; Python compileall: passed; JavaScript syntax: passed; CI, CodeQL, dependency review, workflow security, secret scan, and both container scans: passed..gitmetadata, or paths outside the workspace.CONNECTon port 443 work through the broker while loopback, LAN, private addresses, arbitrary ports, UDP, SSH, proxy bypass variables, and raw container networking remain unavailable.ENABLE FULL ACCESSenables it; the red Full Access indicator remains visible; restart resets to Sandbox; capability failure never enables Full Access automatically.Not run: final stock Ubuntu 24.04 plain-Compose runtime, native ARM64 execution, native macOS/Windows Sandbox mode, or end-to-end browser/UI validation. Independent latest-head security review remains appropriate for this security-sensitive boundary.
Visual / UI changes — REQUIRED if you touched anything that renders
This PR now changes Settings → Tools by adding the process-capability/status card, Sandbox/Full Access control, warning and typed-confirmation flow, and persistent red Full Access indicator.
Screenshots / clips
Pending running-app desktop and mobile evidence.